﻿using System;
using System.Windows.Input;

namespace FileClient.Wpf.ViewModels;

/// <summary>
/// The base implementation of a command.
/// </summary>
public abstract class CommandBase : ICommand
{
    /// <summary>
    /// Occurs when changes occur that affect whether or not the command should execute.
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add { System.Windows.Input.CommandManager.RequerySuggested += value; }
        remove { System.Windows.Input.CommandManager.RequerySuggested -= value; }
    }

    /// <summary>
    /// Raises the <see cref="CanExecuteChanged" /> event.
    /// </summary>
    public void OnCanExecuteChanged()
    {
        System.Windows.Input.CommandManager.InvalidateRequerySuggested();
    }

    /// <summary>
    /// Defines the method that determines whether the command can execute in its current state.
    /// </summary>
    /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    /// <returns>
    /// true if this command can be executed; otherwise, false.
    /// </returns>
    public virtual bool CanExecute(object parameter)
    {
        return true;
    }

    /// <summary>
    /// Defines the method to be called when the command is invoked.
    /// </summary>
    /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    public void Execute(object parameter)
    {
        if (!CanExecute(parameter))
        {
            return;
        }

        OnExecute(parameter);
    }

    /// <summary>
    /// Executes the command.
    /// </summary>
    /// <param name="parameter">The parameter.</param>
    protected abstract void OnExecute(object parameter);
}

/// <summary>
/// The command that relays its functionality by invoking delegates.
/// </summary>
public class RelayCommand : CommandBase
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    /// <summary>
    /// Initializes a new instance of the <see cref="RelayCommand"/> class.
    /// </summary>
    /// <param name="execute">The execute.</param>
    /// <param name="canExecute">The can execute.</param>
    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        if (canExecute == null)
        {
            // no can execute provided, then always executable
            canExecute = (o) => true;
        }

        this._execute = execute;
        this._canExecute = canExecute;
    }

    /// <summary>
    /// Defines the method that determines whether the command can execute in its current state.
    /// </summary>
    /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
    /// <returns>
    /// true if this command can be executed; otherwise, false.
    /// </returns>
    public override bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute.Invoke(parameter);
    }

    /// <summary>
    /// Executes the command.
    /// </summary>
    /// <param name="parameter">The parameter.</param>
    protected override void OnExecute(object parameter)
    {
        _execute?.Invoke(parameter);
    }
}